//
// (C) Copyright 2009 Irantha Suwandarathna (irantha@gmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


using System;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using EffiProz.Core.Lib;
using EffiProz.Core.Errors;
using EffiProz.Core.DataTypes;


namespace EffiProz.Core.Rights
{

    /**
     * A Grantee Object holds the name, access and administrative rights for a
     * particular grantee.<p>
     * It supplies the methods used to grant, revoke, test
     * and check a grantee's access rights to other database objects.
     * It also holds a reference to the common PUBLIC User Object,
     * which represent the special user refered to in
     * GRANT ... TO PUBLIC statements.<p>
     * The check(), isAccessible() and getGrantedClassNames() methods check the
     * rights granted to the PUBLIC User Object, in addition to individually
     * granted rights, in order to decide which rights exist for the user.
     *
     * Method names ending in Direct indicate methods which do not recurse
     * to look through Roles which "this" object is a member of.
     *
     * We use the word "Admin" (e.g., in private variable "admin" and method
     * "isAdmin()) to mean this Grantee has admin priv by any means.
     * We use the word "adminDirect" (e.g., in private variable "adminDirect"
     * and method "isAdminDirect()) to mean this Grantee has admin priv
     * directly.
     *
     * @author Campbell Boucher-Burnett (boucherb@users dot sourceforge.net)
     * @author Fred Toussi (fredt@users dot sourceforge.net)
     * @author Blaine Simpson (blaine dot simpson at admc dot com)
     *
     * @version 1.9.0
     * @since 1.8.0
     */
    public class Grantee : SchemaObject
    {

        public bool _isRole;

        /**
         * true if this grantee has database administrator priv directly
         *  (ie., not by membership in any role)
         */
        private bool isAdminDirect = false;

        /** true if this grantee has database administrator priv by any means. */
        private bool _isAdmin = false;

        /** true if this user can create schemas with its own authorisation */
        public bool _isSchemaCreator = false;

        /** true if this grantee is PUBLIC. */
        public bool _isPublic = false;

        /** true if this grantee is _SYSTEM. */
        public bool _isSystem = false;

        /** Grantee name. */
        protected QNameManager.QName granteeName;

        /** map with database object identifier keys and access privileges values */
        private MultiValueHashMap directRightsMap;

        /** contains righs granted direct, or via roles, expept those of PUBLIC */
        private HashMap fullRightsMap;

        /** These are the DIRECT roles.  Each of these may contain nested roles */
        public OrderedHashSet roles;

        /** map with database object identifier keys and access privileges values */
        private MultiValueHashMap grantedRightsMap;

        /** Needed only to give access to the roles for this database */
        protected GranteeManager granteeManager;

        /**  */
        protected Right ownerRights;

        /**
         * Constructor.
         */
        public Grantee(QNameManager.QName name, GranteeManager man)
        {

            fullRightsMap = new HashMap();
            directRightsMap = new MultiValueHashMap();
            grantedRightsMap = new MultiValueHashMap();
            granteeName = name;
            granteeManager = man;
            roles = new OrderedHashSet();
            ownerRights = new Right();
            ownerRights._isFull = true;
            ownerRights.grantor = GranteeManager.systemAuthorisation;
            ownerRights.grantee = this;
        }

        public int getType()
        {
            return SchemaObjectTypes.GRANTEE;
        }

        public QNameManager.QName getName()
        {
            return granteeName;
        }

        public QNameManager.QName getSchemaName()
        {
            return null;
        }

        public QNameManager.QName getCatalogName()
        {
            return null;
        }

        public Grantee getOwner()
        {
            return null;
        }

        public OrderedHashSet getReferences()
        {
            return null;
        }

        public OrderedHashSet getComponents()
        {
            return null;
        }

        public void compile(Session session, SchemaObject parentObject) { }

        public virtual String getSQL()
        {

            StringBuilder sb = new StringBuilder();

            sb.Append(Tokens.T_CREATE).Append(' ').Append(Tokens.T_ROLE);
            sb.Append(' ').Append(granteeName.statementName);

            return sb.ToString();
        }

        public String getNameString()
        {
            return granteeName.name;
        }

        public String getOriginalNameString()
        {
            return granteeName.originalName;
        }

        public String getStatementName()
        {
            return granteeName.statementName;
        }

        public bool isRole()
        {
            return _isRole;
        }

        public bool isSystem()
        {
            return _isSystem;
        }

        /**
         * Retrieves the map object that represents the rights that have been
         * granted on database objects.  <p>
         *
         * The map has keys and values with the following interpretation: <P>
         *
         * <UL>
         * <LI> The keys are generally (but not limited to) objects having
         *      an attribute or value equal to the name of an actual database
         *      object.
         *
         * <LI> Specifically, the keys act as database object identifiers.
         *
         * <LI> The values are Right objects.
         * </UL>
         */
        public MultiValueHashMap getRights()
        {

            // necessary to create the script
            return directRightsMap;
        }

        /**
         * Grant a role
         */
        public void grant(Grantee role)
        {
            roles.add(role);
        }

        /**
         * Revoke a direct role only
         */
        public void revoke(Grantee role)
        {

            if (!hasRoleDirect(role))
            {
                throw Error.error(ErrorCode.X_0P503, role.getNameString());
            }

            roles.remove(role);
        }

        /**
         * Gets direct roles, not roles nested within them.
         */
        public OrderedHashSet getDirectRoles()
        {
            return roles;
        }

        String getAllRolesAsString()
        {
            return roleMapToString(getAllRoles());
        }

        public String getDirectRolesAsString()
        {
            return roleMapToString(roles);
        }

        public String roleMapToString(OrderedHashSet roles)
        {

            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < roles.size(); i++)
            {
                if (sb.Length > 0)
                {
                    sb.Append(',');
                }

                Grantee role = (Grantee)roles.get(i);

                sb.Append(role.getStatementName());
            }

            return sb.ToString();
        }

        /**
         * Gets direct and indirect roles.
         */
        public OrderedHashSet getAllRoles()
        {

            OrderedHashSet set = getGranteeAndAllRoles();

            // Since we added "Grantee" in addition to Roles, need to remove self.
            set.remove(this);

            return set;
        }

        public OrderedHashSet getGranteeAndAllRoles()
        {

            OrderedHashSet set = new OrderedHashSet();

            addGranteeAndRoles(set);

            return set;
        }

        public OrderedHashSet getGranteeAndAllRolesWithPublic()
        {

            OrderedHashSet set = new OrderedHashSet();

            addGranteeAndRoles(set);
            set.add(granteeManager.publicRole);

            return set;
        }

        /**
         * Adds to given Set this.sName plus all roles and nested roles.
         *
         * @return Given role with new elements added.
         */
        private OrderedHashSet addGranteeAndRoles(OrderedHashSet set)
        {

            Grantee candidateRole;

            set.add(this);

            for (int i = 0; i < roles.size(); i++)
            {
                candidateRole = (Grantee)roles.get(i);

                if (!set.contains(candidateRole))
                {
                    candidateRole.addGranteeAndRoles(set);
                }
            }

            return set;
        }

        /**
         * returns a map with grantee name keys and sets of granted roles as value
         */
        public void addAllRoles(HashMap map)
        {

            for (int i = 0; i < roles.size(); i++)
            {
                Grantee role = (Grantee)roles.get(i);

                map.put(role.granteeName.name, role.roles);
            }
        }

        public bool hasRoleDirect(Grantee role)
        {
            return roles.contains(role);
        }

        public bool hasRole(Grantee role)
        {
            return getAllRoles().contains(role);
        }

        /**
         * Grants the specified rights on the specified database object. <p>
         *
         * Keys stored in rightsMap for database tables are their HsqlName
         * attribute. This allows rights to persist when a table is renamed. <p>
         */
        public void grant(QNameManager.QName name, Right right, Grantee grantor,
                   bool withGrant)
        {

            Right grantableRights = grantor.getAllGrantableRights(name);
            Right existingRight = null;

            if (right == Right.fullRights)
            {
                if (grantableRights.isEmpty())
                {
                    return;    // has no rights
                }

                right = grantableRights;
            }
            else
            {
                if (!grantableRights.contains(right))
                {
                    throw Error.error(ErrorCode.X_0L000);
                }
            }

            Iterator it = directRightsMap.get(name);

            while (it.hasNext())
            {
                Right existing = (Right)it.next();

                if (existing.grantor == grantor)
                {
                    existingRight = existing;

                    existingRight.add(right);

                    break;
                }
            }

            if (existingRight == null)
            {
                existingRight = right.duplicate();
                existingRight.grantor = grantor;
                existingRight.grantee = this;

                directRightsMap.put(name, existingRight);
            }

            if (withGrant)
            {
                if (existingRight.grantableRights == null)
                {
                    existingRight.grantableRights = right.duplicate();
                }
                else
                {
                    existingRight.grantableRights.add(right);
                }
            }

            if (!grantor._isSystem)
            {

                // based on assumption that there is no need to access
                grantor.grantedRightsMap.put(name, existingRight);
            }

            updateAllRights();
        }

        /**
         * Revokes the specified rights on the specified database object. <p>
         *
         * If, after removing the specified rights, no rights remain on the
         * database object, then the key/value pair for that object is removed
         * from the rights map
         */
        public void revoke(SchemaObject _object, Right right, Grantee grantor,
                    bool grantOption)
        {

            QNameManager.QName name = _object.getType() == SchemaObjectTypes.SPECIFIC_ROUTINE
                            ? ((Routine)_object).getSpecificName()
                            : _object.getName();
            Iterator it = directRightsMap.get(name);
            Right existing = null;

            while (it.hasNext())
            {
                existing = (Right)it.next();

                if (existing.grantor == grantor)
                {
                    break;
                }
            }

            if (existing == null)
            {
                return;
            }

            if (existing.grantableRights != null)
            {
                existing.grantableRights.remove(_object, right);
            }

            if (grantOption)
            {
                return;
            }

            if (right._isFull)
            {
                directRightsMap.remove(name, existing);
                grantor.grantedRightsMap.remove(name, existing);
                updateAllRights();

                return;
            }

            existing.remove(_object, right);

            if (existing.isEmpty())
            {
                directRightsMap.remove(name, existing);
                grantor.grantedRightsMap.remove(name, existing);
            }

            updateAllRights();

            return;
        }

        /**
         * Revokes all rights on the specified database object.<p>
         *
         * This method removes any existing mapping from the rights map
         */
        public void revokeDbObject(QNameManager.QName name)
        {

            directRightsMap.remove(name);
            grantedRightsMap.remove(name);
            fullRightsMap.remove(name);
        }

        /**
         * Revokes all rights from this Grantee object.  The map is cleared and
         * the database administrator role attribute is set false.
         */
        public void clearPrivileges()
        {

            roles.clear();
            directRightsMap.clear();
            grantedRightsMap.clear();
            fullRightsMap.clear();

            _isAdmin = false;
        }

        public OrderedHashSet getColumnsForAllPrivileges(Table table)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return table.getColumnNameSet();
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            return right == null ? Right.emptySet
                                 : right.getColumnsForAllRights(table);
        }

        public OrderedHashSet getAllDirectPrivileges(SchemaObject _object)
        {

            if (_object.getOwner() == this)
            {
                OrderedHashSet set = new OrderedHashSet();

                set.add(ownerRights);

                return set;
            }

            Iterator rights = directRightsMap.get(_object.getName());

            if (rights.hasNext())
            {
                OrderedHashSet set = new OrderedHashSet();

                while (rights.hasNext())
                {
                    set.add(rights.next());
                }

                return set;
            }

            return Right.emptySet;
        }

        public OrderedHashSet getAllGrantedPrivileges(SchemaObject _object)
        {

            Iterator rights = grantedRightsMap.get(_object.getName());

            if (rights.hasNext())
            {
                OrderedHashSet set = new OrderedHashSet();

                while (rights.hasNext())
                {
                    set.add(rights.next());
                }

                return set;
            }

            return Right.emptySet;
        }

        /**
         * Checks if a right represented by the methods
         * have been granted on the specified database object. <p>
         *
         * This is done by checking that a mapping exists in the rights map
         * from the dbobject argument. Otherwise, it throws.
         */
        public void checkSelect(Table table, bool[] checkList)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return;
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            if (right != null && right.canSelect(table, checkList))
            {
                return;
            }

            throw Error.error(ErrorCode.X_42501, table.getName().name);
        }

        public void checkInsert(Table table, bool[] checkList)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return;
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            if (right != null && right.canInsert(table, checkList))
            {
                return;
            }

            throw Error.error(ErrorCode.X_42501, table.getName().name);
        }

        public void checkUpdate(Table table, bool[] checkList)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return;
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            if (right != null && right.canUpdate(table, checkList))
            {
                return;
            }

            throw Error.error(ErrorCode.X_42501, table.getName().name);
        }

        public void checkReferences(Table table, bool[] checkList)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return;
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            if (right != null && right.canReference(table, checkList))
            {
                return;
            }

            throw Error.error(ErrorCode.X_42501, table.getName().name);
        }

        public void checkTrigger(Table table, bool[] checkList)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return;
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            if (right != null && right.canReference(table, checkList))
            {
                return;
            }

            throw Error.error(ErrorCode.X_42501, table.getName().name);
        }

        public void checkDelete(Table table)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return;
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            if (right != null && right.canDelete())
            {
                return;
            }

            throw Error.error(ErrorCode.X_42501, table.getName().name);
        }

        public void checkAccess(SchemaObject _object)
        {

            if (isFullyAccessibleByRole(_object.getName()))
            {
                return;
            }

            Right right = (Right)fullRightsMap.get(_object.getName());

            if (right != null && !right.isEmpty())
            {
                return;
            }

            throw Error.error(ErrorCode.X_42501, _object.getName().name);
        }

        /**
         * Checks if this object can modify schema objects or grant access rights
         * to them.
         */
        public void checkSchemaUpdateOrGrantRights(String schemaName)
        {

            if (!hasSchemaUpdateOrGrantRights(schemaName))
            {
                throw Error.error(ErrorCode.X_42501, schemaName);
            }
        }

        /**
         * Checks if this object can modify schema objects or grant access rights
         * to them.
         */
        public bool hasSchemaUpdateOrGrantRights(String schemaName)
        {

            // If a DBA
            if (isAdmin())
            {
                return true;
            }

            Grantee schemaOwner =
                granteeManager.database.schemaManager.toSchemaOwner(schemaName);

            // If owner of Schema
            if (schemaOwner == this)
            {
                return true;
            }

            // If a member of Schema authorization role
            if (hasRole(schemaOwner))
            {
                return true;
            }

            return false;
        }

        public bool isGrantable(SchemaObject _object, Right right)
        {

            if (isFullyAccessibleByRole(_object.getName()))
            {
                return true;
            }

            Right grantableRights = getAllGrantableRights(_object.getName());

            return grantableRights.contains(right);
        }

        public bool isGrantable(Grantee role)
        {
            return _isAdmin;
        }


        public bool isFullyAccessibleByRole(QNameManager.QName name)
        {

            if (_isAdmin)
            {
                return true;
            }

            if (name.schema == null)
            {
                return false;
            }

            Grantee owner = name.schema.owner;

            if (owner == this)
            {
                return true;
            }

            if (hasRole(owner))
            {
                return true;
            }

            return false;
        }

        /**
         * Checks whether this Grantee has administrative privs either directly
         * or indirectly. Otherwise it throws.
         */
        public void checkAdmin()
        {

            if (!isAdmin())
            {
                throw Error.error(ErrorCode.X_42507);
            }
        }

        /**
         * Returns true if this Grantee has administrative privs either directly
         * or indirectly.
         */
        public bool isAdmin()
        {
            return _isAdmin;
        }

        /**
         * Returns true if this Grantee can create schemas with own authorization.
         */
        public bool isSchemaCreator()
        {
            return _isAdmin || hasRole(granteeManager.schemaRole);
        }

        /**
         * Returns true if this Grantee can change to a different user.
         */
        public bool canChangeAuthorisation()
        {
            return _isAdmin || hasRole(granteeManager.changeAuthRole);
        }

        /**
         * Returns true if this grantee object is for the PUBLIC role.
         */
        public bool isPublic()
        {
            return _isPublic;
        }

        /**
         * Violates naming convention (for backward compatibility).
         * Should be "setAdminDirect(bool").
         */
        public void setAdminDirect()
        {
            _isAdmin = isAdminDirect = true;
        }

        /**
         * Recursive method used with ROLE Grantee objects to set the fullRightsMap
         * and admin flag for all the roles.
         *
         * If a new ROLE is granted to a ROLE Grantee object, the ROLE should first
         * be added to the Set of ROLE Grantee objects (roles) for the grantee.
         * The grantee will be the parameter.
         *
         * If the direct permissions granted to an existing ROLE Grentee is
         * modified no extra initial action is necessary.
         * The existing Grantee will be the parameter.
         *
         * If an existing ROLE is REVOKEed from a ROLE, it should first be removed
         * from the set of ROLE Grantee objects in the containing ROLE.
         * The containing ROLE will be the parameter.
         *
         * If an existing ROLE is DROPped, all its privileges should be cleared
         * first. The ROLE will be the parameter. After calling this method on
         * all other roles, the DROPped role should be removed from all grantees.
         *
         * After the initial modification, this method should be called iteratively
         * on all the ROLE Grantee objects contained in RoleManager.
         *
         * The updateAllRights() method is then called iteratively on all the
         * USER Grantee objects contained in UserManager.
         * @param role a modified, revoked or dropped role.
         * @return true if this Grantee has possibly changed as a result
         */
        public bool updateNestedRoles(Grantee role)
        {

            bool hasNested = false;

            if (role != this)
            {
                for (int i = 0; i < roles.size(); i++)
                {
                    Grantee currentRole = (Grantee)roles.get(i);

                    hasNested |= currentRole.updateNestedRoles(role);
                }
            }

            if (hasNested)
            {
                updateAllRights();
            }

            return hasNested || role == this;
        }

        /**
         * Method used with all Grantee objects to set the full set of rights
         * according to those inherited form ROLE Grantee objects and those
         * granted to the object itself.
         */

        /**
         * @todo -- see if this is correct and the currentRole.fullRightsMap
         * is always updated prior to being added to this.fullRightsMap
         */
        public void updateAllRights()
        {

            fullRightsMap.clear();

            _isAdmin = isAdminDirect;

            for (int i = 0; i < roles.size(); i++)
            {
                Grantee currentRole = (Grantee)roles.get(i);

                addToFullRights(currentRole.fullRightsMap);

                _isAdmin |= currentRole.isAdmin();
            }

            addToFullRights(directRightsMap);

            if (!_isRole && !_isPublic && !_isSystem)
            {
                addToFullRights(granteeManager.publicRole.fullRightsMap);
            }
        }

        /**
         * Full or partial rights are added to existing
         */
        void addToFullRights(HashMap map)
        {

            Iterator it = map.keySet().iterator();

            while (it.hasNext())
            {
                Object key = it.next();
                Right add = (Right)map.get(key);
                Right existing = (Right)fullRightsMap.get(key);

                if (existing == null)
                {
                    existing = add.duplicate();

                    fullRightsMap.put(key, existing);
                }
                else
                {
                    existing.add(add);
                }

                if (add.grantableRights == null)
                {
                    continue;
                }

                if (existing.grantableRights == null)
                {
                    existing.grantableRights = add.grantableRights.duplicate();
                }
                else
                {
                    existing.grantableRights.add(add.grantableRights);
                }
            }
        }

        /**
         * Full or partial rights are added to existing
         */
        void addToFullRights(MultiValueHashMap map)
        {

            Iterator it = map.keySet().iterator();

            while (it.hasNext())
            {
                Object key = it.next();
                Iterator values = map.get(key);
                Right existing = (Right)fullRightsMap.get(key);

                while (values.hasNext())
                {
                    Right add = (Right)values.next();

                    if (existing == null)
                    {
                        existing = add.duplicate();

                        fullRightsMap.put(key, existing);
                    }
                    else
                    {
                        existing.add(add);
                    }

                    if (add.grantableRights == null)
                    {
                        continue;
                    }

                    if (existing.grantableRights == null)
                    {
                        existing.grantableRights = add.grantableRights.duplicate();
                    }
                    else
                    {
                        existing.grantableRights.add(add.grantableRights);
                    }
                }
            }
        }

        /**
         * Iteration of all visible grantees, including self. <p>
         *
         * For grantees with admin, this is all grantees.
         * For regular grantees, this is self plus all roles granted directly
         * or indirectly
         */
        public Set visibleGrantees()
        {

            HashSet grantees = new HashSet();
            GranteeManager gm = granteeManager;

            if (isAdmin())
            {
                grantees.addAll(gm.getGrantees());
            }
            else
            {
                grantees.add(this);

                Iterator it = getAllRoles().iterator();

                while (it.hasNext())
                {
                    grantees.add(it.next());
                }
            }

            return grantees;
        }

        /**
         * Set of all non-reserved visible grantees, including self. <p>
         *
         * For grantees with admin, this is all grantees.
         * For regular grantees, this is self plus all roles granted directly
         * or indirectly. <P>
         *
         * @param andPublic when <tt>true</tt> retains the reserved PUBLIC grantee
         */
        public Set nonReservedVisibleGrantees(bool andPublic)
        {

            Set grantees = visibleGrantees();
            GranteeManager gm = granteeManager;

            grantees.remove(gm.dbaRole);
            grantees.remove(GranteeManager.systemAuthorisation);

            if (!andPublic)
            {
                grantees.remove(gm.publicRole);
            }

            return grantees;
        }

        public bool hasNonSelectTableRight(Table table)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return true;
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            if (right == null)
            {
                return false;
            }

            return right._isFull || right.isFullDelete || right.isFullInsert
                   || right.isFullUpdate || right.isFullReferences
                   || right.isFullTrigger;
        }

        public bool hasTableRight(Table table)
        {

            if (isFullyAccessibleByRole(table.getName()))
            {
                return true;
            }

            Right right = (Right)fullRightsMap.get(table.getName());

            if (right == null)
            {
                return false;
            }

            return right._isFull || right.isFullDelete || right.isFullInsert
                   || right.isFullUpdate || right.isFullReferences
                   || right.isFullTrigger || right.isFullSelect;
        }

        public Iterator getAllDirectFullRights(SchemaObject _object)
        {

            Grantee owner = _object.getOwner();

            if (owner == this)
            {
                return new WrapperIterator(ownerRights);
            }

            return directRightsMap.get(_object.getName());
        }

        public Right getAllGrantableRights(QNameManager.QName name)
        {

            if (_isAdmin)
            {
                return name.schema.owner.ownerRights;
            }

            if (name.schema.owner == this)
            {
                return ownerRights;
            }

            if (roles.contains(name.schema.owner))
            {
                return name.schema.owner.ownerRights;
            }

            OrderedHashSet set = getAllRoles();

            for (int i = 0; i < set.size(); i++)
            {
                Grantee role = (Grantee)set.get(i);

                if (name.schema.owner == role)
                {
                    return role.ownerRights;
                }
            }

            Right right = (Right)fullRightsMap.get(name);

            return right == null || right.grantableRights == null ? Right.noRights
                                                                  : right
                                                                  .grantableRights;
        }

        public bool isAccessible(QNameManager.QName name, int privilegeType)
        {

            if (isFullyAccessibleByRole(name))
            {
                return true;
            }

            Right right = (Right)fullRightsMap.get(name);

            if (right == null)
            {
                return false;
            }

            return right.canAccess(privilegeType);
        }

        /**
         * returns true if grantee has any privilege (to any column) of the object
         */
        public bool isAccessible(SchemaObject _object)
        {
            return isAccessible(_object.getName());
        }

        public bool isAccessible(QNameManager.QName name)
        {

            if (isFullyAccessibleByRole(name))
            {
                return true;
            }

            Right right = (Right)fullRightsMap.get(name);

            if (right != null && !right.isEmpty())
            {
                return true;
            }

            if (!_isPublic)
            {
                return granteeManager.publicRole.isAccessible(name);
            }

            return false;
        }

        public EfzArrayList getRightsSQL()
        {

            EfzArrayList list = new EfzArrayList();
            String roleString = getDirectRolesAsString();

            if (roleString.Length != 0)
            {
                StringBuilder sb = new StringBuilder(128);

                sb.Append(Tokens.T_GRANT).Append(' ').Append(roleString);
                sb.Append(' ').Append(Tokens.T_TO).Append(' ');
                sb.Append(getStatementName());
                list.add(sb.ToString());
            }

            MultiValueHashMap rightsMap = getRights();
            Iterator dbObjects = rightsMap.keySet().iterator();

            while (dbObjects.hasNext())
            {
                Object nameObject = dbObjects.next();
                Iterator rights = rightsMap.get(nameObject);

                while (rights.hasNext())
                {
                    Right right = (Right)rights.next();
                    StringBuilder sb = new StringBuilder(128);
                    QNameManager.QName hsqlname = (QNameManager.QName)nameObject;

                    switch (hsqlname.type)
                    {

                        case SchemaObjectTypes.TABLE:
                        case SchemaObjectTypes.VIEW:
                            Table table =
                                granteeManager.database.schemaManager
                                    .findUserTable(null, hsqlname.name,
                                                   hsqlname.schema.name);

                            if (table != null)
                            {
                                sb.Append(Tokens.T_GRANT).Append(' ');
                                sb.Append(right.getTableRightsSQL(table));
                                sb.Append(' ').Append(Tokens.T_ON).Append(' ');
                                sb.Append(Tokens.T_TABLE).Append(' ');
                                sb.Append(
                                    hsqlname.getSchemaQualifiedStatementName());
                            }
                            break;

                        case SchemaObjectTypes.SEQUENCE:
                            NumberSequence sequence =
                                (NumberSequence)granteeManager.database
                                    .schemaManager
                                    .findSchemaObject(hsqlname.name,
                                                      hsqlname.schema.name,
                                                      SchemaObjectTypes.SEQUENCE);

                            if (sequence != null)
                            {
                                sb.Append(Tokens.T_GRANT).Append(' ');
                                sb.Append(Tokens.T_USAGE);
                                sb.Append(' ').Append(Tokens.T_ON).Append(' ');
                                sb.Append(Tokens.T_SEQUENCE).Append(' ');
                                sb.Append(
                                    hsqlname.getSchemaQualifiedStatementName());
                            }
                            break;

                        case SchemaObjectTypes.DOMAIN:
                            SqlType domain =
                                (SqlType)granteeManager.database.schemaManager
                                    .findSchemaObject(hsqlname.name,
                                                      hsqlname.schema.name,
                                                      SchemaObjectTypes.DOMAIN);

                            if (domain != null)
                            {
                                sb.Append(Tokens.T_GRANT).Append(' ');
                                sb.Append(Tokens.T_USAGE);
                                sb.Append(' ').Append(Tokens.T_ON).Append(' ');
                                sb.Append(Tokens.T_DOMAIN).Append(' ');
                                sb.Append(
                                    hsqlname.getSchemaQualifiedStatementName());
                            }
                            break;

                        case SchemaObjectTypes.TYPE:
                            SqlType type =
                                (SqlType)granteeManager.database.schemaManager
                                    .findSchemaObject(hsqlname.name,
                                                      hsqlname.schema.name,
                                                      SchemaObjectTypes.DOMAIN);

                            if (type != null)
                            {
                                sb.Append(Tokens.T_GRANT).Append(' ');
                                sb.Append(Tokens.T_USAGE);
                                sb.Append(' ').Append(Tokens.T_ON).Append(' ');
                                sb.Append(Tokens.T_TYPE).Append(' ');
                                sb.Append(
                                    hsqlname.getSchemaQualifiedStatementName());
                            }
                            break;

                        case SchemaObjectTypes.PROCEDURE:
                        case SchemaObjectTypes.FUNCTION:
                        case SchemaObjectTypes.SPECIFIC_ROUTINE:
                            SchemaObject routine =
                                (SchemaObject)granteeManager.database
                                    .schemaManager
                                    .findSchemaObject(hsqlname.name,
                                                      hsqlname.schema.name,
                                                      hsqlname.type);

                            if (routine != null)
                            {
                                sb.Append(Tokens.T_GRANT).Append(' ');
                                sb.Append(Tokens.T_EXECUTE).Append(' ');
                                sb.Append(Tokens.T_ON).Append(' ');
                                sb.Append(Tokens.T_SPECIFIC).Append(' ');

                                if (routine.getType() == SchemaObjectTypes.PROCEDURE)
                                {
                                    sb.Append(Tokens.T_PROCEDURE);
                                }
                                else
                                {
                                    sb.Append(Tokens.T_FUNCTION);
                                }

                                sb.Append(' ');
                                sb.Append(
                                    hsqlname.getSchemaQualifiedStatementName());
                            }
                            break;
                    }

                    if (sb.Length == 0)
                    {
                        continue;
                    }

                    sb.Append(' ').Append(Tokens.T_TO).Append(' ');
                    sb.Append(getStatementName());
                    list.add(sb.ToString());
                }
            }

            return list;
        }
    }
}
